package com.cloud.base.service.impl;

import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.DesensitizedUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.poi.excel.ExcelReader;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import cn.hutool.poi.excel.StyleSet;
import com.alibaba.csp.sentinel.util.StringUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cloud.api.constants.RedisConstants;
import com.cloud.api.constants.StrVo;
import com.cloud.api.domain.sys.*;
import com.cloud.api.utils.WebHttpApi;
import com.cloud.base.mapper.SysDeptMapper;
import com.cloud.base.service.ISysDeptService;
import com.cloud.core.exception.MyException;
import com.cloud.core.util.R;
import com.cloud.mybatis.domain.TableDataInfo;
import jakarta.annotation.Resource;
import jakarta.servlet.ServletOutputStream;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.beans.BeanUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;


/**
 * <p>
 * 部门表 服务实现类
 * </p>
 *
 * @author haohaibo
 * @since 2023-08-01
 */
@Service("sysDeptService")
public class SysDeptServiceImpl extends ServiceImpl<SysDeptMapper, SysDept> implements ISysDeptService {

    @Resource
    private SysDeptMapper sysDeptMapper;
    @Resource(name = "myRedis")
    private RedisTemplate<String, Object> redisTemplate;

    @Override
    public TableDataInfo<SysDeptDto> selectDeptList(SysDeptQuery para) {
        Page<SysDeptDto> page = new Page<>();
        page.setCurrent(para.getPage());
        page.setSize(para.getPageSize());
        IPage<SysDeptDto> data = sysDeptMapper.selectDeptList(page, para);
        List<SysDeptDto> records = data.getRecords();
        List<SysDeptDto> list = parseUser(records);
        return TableDataInfo.build(list);
    }

    public List<SysDeptDto> parseUser(List<SysDeptDto> records){
        for (SysDeptDto sysDeptDto : records){
            if (StringUtil.isNotBlank(sysDeptDto.getPhone())){
                sysDeptDto.setPhone(DesensitizedUtil.mobilePhone(sysDeptDto.getPhone()));
            }
            if (StringUtil.isNotBlank(sysDeptDto.getUpdateUser())){
                SystemUser systemUser = (SystemUser) redisTemplate.opsForValue().get(RedisConstants.USER_ID + sysDeptDto.getUpdateUser());
                if (systemUser != null){
                    sysDeptDto.setUpdateUser(systemUser.getNickName());
                }
            }
            sysDeptDto.setChildren(parseUser(sysDeptDto.getChildren()));
        }
        return records;
    }


    @Override
    public List<SysDeptDto> getDeptListEnable() {
        return sysDeptMapper.getDeptListEnable();
    }

    @Override
    public List<SysDept> selectServiceByAuth(String type) {
        /*LambdaQueryWrapper<SysDept> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(ObjectUtil.isNotNull(type),SysDept::getDeptType,type)
                .eq(SysDept::getEnable,0)
                .select(SysDept::getDeptId,SysDept::getDeptName,SysDept::getParentId);*/
//        List<SysDept>sysDepts = new ArrayList<>();
        /*String roleId = LoginHelper.getUserLogin().getRoleId();
        String deptId = LoginHelper.getUserLogin().getDeptId();
        Integer dataScope = LoginHelper.getDataScope();
        switch (dataScope){
            case 1:
                sysDepts = sysDeptMapper.selectList(queryWrapper);
                break;
            case 2:
                sysDepts.add(sysDeptMapper.selectById(deptId));
            case 5:
                sysDepts = sysDeptMapper.selectDeptsList(roleId,type);
                break;
            case 3:
               *//* LambdaQueryWrapper<RoleDept> queryRoleDept = new LambdaQueryWrapper<>();
                queryRoleDept.eq(RoleDept::getRoleId,roleId);
                List<RoleDept> list = roleDeptService.list(queryRoleDept);
                List<String> deptIds = list.stream().map(RoleDept::getDeptId).collect(Collectors.toList());*//*
                sysDepts = sysDeptMapper.selectDeptAndFollowDept(deptId,type);
                break;
            default:
                break;
        }*/
        return sysDeptMapper.selectAuthLList(type);
    }

    @Override
    public TableDataInfo<SysDept> getDeptListByCondition(SysDeptQuery para) {
        Page<SysDept> page = new Page<>();
        page.setCurrent(para.getPage());
        page.setSize(para.getPageSize());
        IPage<SysDept> data = sysDeptMapper.getDeptListByCondition(page, para);
        for (SysDept sysDept : data.getRecords()){
            if (StringUtil.isNotBlank(sysDept.getPhone())){
                sysDept.setPhone(DesensitizedUtil.mobilePhone(sysDept.getPhone()));
            }
        }
        return TableDataInfo.build(data);
    }

    @Override
    public void insertOrUpdate(SysDept sysDept) {
        if (sysDept.getDeptId() == null && (StringUtils.isBlank(sysDept.getParentId()) ||  sysDept.getParentId().equals("0"))){
            LambdaQueryWrapper<SysDept> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(SysDept::getParentId,"0");
            Long count = sysDeptMapper.selectCount(queryWrapper);
            if (count >= 1){
                throw new MyException("wds.exception.bigDept");
            }
        }
        verifyParentId(sysDept);
        SysDept info = sysDeptMapper.selectById(sysDept.getParentId());
        if (info != null){
            if (info.getAncestors() != null && !"".equals(info.getAncestors())){
                sysDept.setAncestors(info.getAncestors() + ',' + sysDept.getParentId());
            }else {
                sysDept.setAncestors(sysDept.getParentId());
            }
        }
        DeptExtend deptExtend = new DeptExtend();
        if (StringUtil.isBlank(sysDept.getDeptId())){
            sysDept.setDeptId(IdUtil.getSnowflakeNextIdStr());
            if (sysDept.getEnable() == null){
                sysDept.setEnable(0);
            }
            deptExtend.setStatus(0);
        }
        if (!"org".equals(sysDept.getDeptType())){
            deptExtend.setDeptId(sysDept.getDeptId());
            deptExtend.setParentId(sysDept.getParentId());
            deptExtend.setDeptName(sysDept.getDeptName());
            deptExtend.setDeptType(sysDept.getDeptType());
            deptExtend.setContactMan(sysDept.getLeader());
            deptExtend.setContact(sysDept.getPhone());
            deptExtend.setDescription(sysDept.getDescription());
            WebHttpApi.post("http://tsp/tsp/dept-extend/insertOrUpdateForDep", deptExtend);
        }
        redisTemplate.opsForHash().put("DEPT-NAME",sysDept.getDeptId(),sysDept.getDeptName());
        this.saveOrUpdate(sysDept);
    }

    public void verifyParentId(SysDept sysDept){
        if (StringUtil.isBlank(sysDept.getDeptId())){
            return;
        }
        String deptId = sysDept.getDeptId();
        if ("".equals(sysDept.getParentId())){
            throw new MyException("wds.exception.seniorDeptNull");
        }
        if (deptId.equals(sysDept.getParentId())){
            throw new MyException("wds.exception.seniorDeptSelf");
        }
        SysDept query = sysDeptMapper.containDeptId(sysDept);
        if (query != null){
            throw new MyException("wds.exception.seniorDeptSon");
        }

    }

    @Override
    public void importDeptList(String parentId,MultipartFile file) throws IOException {

        List<String> list = readExcelHeaders(file);
        List<String> header = Arrays.asList("组织机构名称", "状态", "机构类型", "联系人", "联系电话", "说明");
        if (!header.equals(list)) {
            throw new MyException("wds.exception.importHeaderError");
        }

        SysDept info = sysDeptMapper.selectById(parentId);
        String ancestors = parentId;
        if (info != null && StringUtil.isNotBlank(info.getAncestors())){
            ancestors = info.getAncestors() + ',' + parentId;
        }
        ExcelReader reader = ExcelUtil.getReader(file.getInputStream());
        reader.addHeaderAlias("组织机构名称","deptName")
                .addHeaderAlias("状态","enable")
                .addHeaderAlias("机构类型","deptType")
                .addHeaderAlias("联系人","leader")
                .addHeaderAlias("联系电话","phone")
                .addHeaderAlias("说明","description");
        List<SysDept> sysDepts = reader.read(1,2,SysDept.class);
        String finalAncestors = ancestors;
        List<DeptExtend> deptExtends = new ArrayList<>();
        sysDepts.forEach(a -> {
            a.setDeptId(IdUtil.getSnowflakeNextIdStr());
            a.setParentId(parentId);
            a.setAncestors(finalAncestors);
            if (!"org".equals(a.getDeptType())){
                DeptExtend deptExtend = new DeptExtend();
                deptExtend.setDeptId(a.getDeptId());
                deptExtend.setDeptName(a.getDeptName());
                deptExtend.setParentId(parentId);
                deptExtend.setDeptType(a.getDeptType());
                deptExtend.setContactMan(a.getLeader());
                deptExtend.setContact(a.getPhone());
                deptExtend.setDescription(a.getDescription());
                deptExtends.add(deptExtend);
            }
        });
        this.saveBatch(sysDepts);
        if (deptExtends.size() > 0){
            WebHttpApi.post("http://tsp/tsp/dept-extend/insertBatch", deptExtends);
        }
    }

    private static List<String> readExcelHeaders(MultipartFile file) throws IOException {
        List<String> headers = new ArrayList<>();
        Workbook workbook = new XSSFWorkbook(file.getInputStream());
        Sheet sheet = workbook.getSheetAt(0);
        Row headerRow = sheet.getRow(1);

        for (Cell cell : headerRow) {
            CellType cellType = cell.getCellType();
            if (cellType== CellType.STRING){
                headers.add(cell.getStringCellValue());
            }
        }
        workbook.close();
        return headers;
    }

    @Override
    public void downloadTemplate(HttpServletResponse response) throws IOException {
        ExcelWriter excelWriter = ExcelUtil.getWriter("组织机构信息表.xlsx");
        excelWriter.writeRow(Arrays.asList("","状态填写0或1：0正常 1停用","组织机构填写英文：org/组织，sale/经销商，service/服务站"));
        StyleSet styleSet = excelWriter.getStyleSet();
        styleSet.setBackgroundColor(IndexedColors.GREY_25_PERCENT,false);
        excelWriter.writeCellValue(0,0,"第一行勿删");
        Font font = excelWriter.createFont();
        font.setColor(Font.COLOR_RED);
        excelWriter.writeRow(Arrays.asList("组织机构名称","状态","机构类型","联系人","联系电话","说明"));
        CellStyle cellStyle = excelWriter.createCellStyle(0, 0);
        cellStyle.setFont(font);
        cellStyle.setAlignment(HorizontalAlignment.CENTER);
        response.setContentType("application/vnd.ms-excel;charset=utf-8");
        response.setHeader("Content-Disposition", "attachment;filename*=" + URLEncoder.encode("组织机构信息表.xlsx", "UTF-8"));
        try {
            ServletOutputStream out=response.getOutputStream();
            excelWriter.flush(out,true);
            excelWriter.close();
            IoUtil.close(out);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void deleteById(String deptId) {
        LambdaQueryWrapper<SysDept> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.like(SysDept::getAncestors,deptId);
        SysDept sysDept = sysDeptMapper.selectById(deptId);
        if ("0".equals(sysDept.getParentId())){
            throw new MyException("wds.exception.deleteBigDept");
        }
        Long count = sysDeptMapper.selectCount(queryWrapper);
        if (count > 0){
            throw new MyException("wds.exception.deleteDeptContainSon");
        }
        R<Integer> r = WebHttpApi.post("http://tsp/tsp/vehicle-sale/getVehicleCountByDept",Collections.singletonList(deptId),null,R.class);
        if (r.getData() > 0){
            throw new MyException("wds.exception.deleteDeptContainVehicle");
        }
        if (!"org".equals(sysDept.getDeptType())){
            WebHttpApi.post("http://tsp/tsp/dept-extend/deleteByIdForDep",new StrVo(deptId));
        }
        sysDeptMapper.deleteById(deptId);
    }
    public boolean checkExist(CheckName para) {
        final boolean[] result = {true};
        LambdaQueryWrapper<SysDept> wrapper = new QueryWrapper().lambda();
        wrapper.eq(ObjectUtil.isNotNull(para.getName()), SysDept::getDeptName, para.getName());
        List<SysDept> sysDepts = sysDeptMapper.selectList(wrapper);
        sysDepts.stream().filter(a -> !a.getDeptId().equals(para.getId()))
                .findFirst()
                .ifPresent(a -> {
                    result[0] = false;
                    return;
                });
        return result[0];
    }
    @Override
    public void deleteByIds(String deptIds) {
        String[] idList = deptIds.split(",");
        List<String> list = new ArrayList<>(Arrays.asList(idList));
        LambdaQueryWrapper<SysDept> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.orderByAsc(SysDept::getParentId);
        List<SysDept> sysDepts = sysDeptMapper.selectList(queryWrapper);
        for (String depId : list){
            boolean isTopDept = sysDepts.stream().anyMatch(a -> depId.equals(a.getDeptId()) && "0".equals(a.getParentId()));
            if (isTopDept){
                throw new MyException("wds.exception.deleteContainBigDept");
            }
            boolean isParentDept = sysDepts.stream().anyMatch(a -> depId.equals(a.getParentId()));
            if (isParentDept){
                throw new MyException("wds.exception.deleteDeptContainSon");
            }
        }
        R<Integer> r = WebHttpApi.post("http://tsp/tsp/vehicle-sale/getVehicleCountByDept",list,null,R.class);
        if (r.getData() > 0){
            throw new MyException("wds.exception.deleteDeptContainVehicle");
        }
        for (String depId : list){
            redisTemplate.opsForHash().delete("DEPT-NAME",depId);
        }
        sysDeptMapper.deleteBatchIds(list);
    }

    @Override
    public void deleteByIdForExtend(String deptId) {
        LambdaQueryWrapper<SysDept> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.like(SysDept::getAncestors,deptId);
        Long count = sysDeptMapper.selectCount(queryWrapper);
        if (count > 0){
            throw new MyException("wds.exception.deleteDeptContainSon");
        }
        sysDeptMapper.deleteById(deptId);
    }

    /*@Override
    public List<SysDept> selectAllDeptAndFollow() {
        String deptId = LoginHelper.getUserLogin().getDeptId();
        return sysDeptMapper.selectAllDeptAndFollow(deptId);
    }*/

    @Override
    public List<SysDeptDto> selectServiceByAuthTree() {
        List<SysDept> list = this.selectServiceByAuth(null);
        List<SysDeptDto> sysDeptDtos = new ArrayList<>();
        for (SysDept sysDept : list){
            SysDeptDto sysDeptDto = new SysDeptDto();
            BeanUtils.copyProperties(sysDept,sysDeptDto);
            sysDeptDto.setPhone(DesensitizedUtil.mobilePhone(sysDept.getPhone()));
            sysDeptDtos.add(sysDeptDto);
        }
        List<SysDeptDto> tree = createTree(sysDeptDtos);
        return tree;
    }

    public List<SysDeptDto> createTree(List<SysDeptDto> list){
            List<SysDeptDto> rootTree = new ArrayList<>();
            for (SysDeptDto tree : list) {
                // 第一步 筛选出最顶级的父节点
                if ("0".equals(tree.getParentId())) {
                    rootTree.add(tree);
                }
                // 第二步 筛选出该父节点下的所有子节点列表
                for (SysDeptDto node : list) {
                    if (node.getParentId().equals(tree.getDeptId())) {
                        if (CollectionUtils.isEmpty(tree.getChildren())) {
                            tree.setChildren(new ArrayList<>());
                        }
                        tree.getChildren().add(node);
                    }
                }
            }
            return rootTree;
    }
}